#ifndef _BOX_H_
#define _BOX_H_

#include "../cuCommon/cuTypes.h"
#include "../cuCommon/cuMath.h"

struct S_Box
{
  float3 pmin;
  float3 pmax;

	S_Box()
	{
		pmax = f3();
		pmin = f3();
	}

	S_Box(const float3 & pmin, const float3 & pmax)
	{
		this->pmin = pmin;
		this->pmax = pmax;
	}

	S_Box(const float3 & v)
	{
		this->pmax = this->pmin = v;
	}

	S_Box(float2 pV[])
	{
		pmin.x = pV[0].x; pmax.x = pV[0].y; 
		pmin.y = pV[1].x; pmax.y = pV[1].y; 
		pmin.z = pV[2].x; pmax.z = pV[2].y; 
	}

	inline float2 Ix() { return f2(pmin.x, pmax.x); }
	inline float2 Iy() { return f2(pmin.y, pmax.y); }
	inline float2 Iz() { return f2(pmin.z, pmax.z); }

	inline void Ix(const float2 & iv) { pmin.x = iv.x; pmax.x = iv.y; }
	inline void Iy(const float2 & iv) { pmin.y = iv.x; pmax.y = iv.y; }
	inline void Iz(const float2 & iv) { pmin.z = iv.x; pmax.z = iv.y; }

	inline float3 min(const float3 & p) { pmin = ::min(pmin, p); return pmin; }
	inline float3 max(const float3 & p) { pmax = ::max(pmax, p); return pmax; }

	inline float3 min() { return pmin; }
	inline float3 max() { return pmax; }
};

struct SAABoxPacked
{
};

SDH_CALL
S_Box * AABox(S_Box * pBox)
{
  set(pBox->pmin, 0.0f, 0.0f, 0.0f);
  set(pBox->pmax, 1.0f, 1.0f, 1.0f);

  return pBox;
}

SDH_CALL
S_Box * AABox(S_Box * pBox, float3 pmin, float3 pmax)
{
  pBox->pmin = pmin;
  pBox->pmax = pmax;

  return pBox;
}

SDH_CALL
S_Box * AABox(S_Box * pBox, float2 * pInterval)
{
	pBox->pmin.x = pInterval[0].x;
	pBox->pmax.x = pInterval[0].y;

	pBox->pmin.y = pInterval[1].x;
	pBox->pmax.y = pInterval[1].y;

	pBox->pmin.z = pInterval[2].x;
	pBox->pmax.z = pInterval[2].y;

	return pBox;
}

SDH_CALL
S_Box * Transform(S_Box * pDst, S_Box * pSrc, const matrix4x4 & M4x4)
{
	float3 a = pSrc->pmin;
	float3 b = pSrc->pmax;

	float3 ti = f3(f4(a.x, a.y, a.z, 1) * M4x4);

  float3 pmin = ti;
  float3 pmax = ti;

	ti = f3(f4(b.x, a.y, a.z, 1) * M4x4);

	pmin = min(ti, pmin);
	pmax = max(ti, pmax);

	ti = f3(f4(a.x, b.y, a.z, 1) * M4x4);

	pmin = min(ti, pmin);
	pmax = max(ti, pmax);

	ti = f3(f4(a.x, a.y, b.z, 1) * M4x4);

	pmin = min(ti, pmin);
	pmax = max(ti, pmax);

	ti = f3(f4(a.x, b.y, b.z, 1) * M4x4);

	pmin = min(ti, pmin);
	pmax = max(ti, pmax);

	ti = f3(f4(b.x, a.y, b.z, 1) * M4x4);

	pmin = min(ti, pmin);
	pmax = max(ti, pmax);

	ti = f3(f4(b.x, b.y, a.z, 1) * M4x4);

	pmin = min(ti, pmin);
	pmax = max(ti, pmax);

	ti = f3(f4(b.x, b.y, b.z, 1) * M4x4);

	pDst->pmin = min(ti, pmin);
	pDst->pmax = max(ti, pmax);

  return pDst;
}

static int3 CompareTo(const S_Box & aabb, float3 f3, int3 Dim)
{
  float3 pmin = aabb.pmin;
  float3 pmax = aabb.pmax;

  float3 l3 = pmax - pmin;

  int3 id;

  id.x = (int)((f3.x - pmin.x) / l3.x * Dim.x);
  id.y = (int)((f3.y - pmin.y) / l3.y * Dim.y);
  id.z = (int)((f3.z - pmin.z) / l3.z * Dim.z);

  return id;
}

static int3 CompareTo(const S_Box &  aabb, float3 v[3], int3 Dim)
{
  float3 pmin = aabb.pmin;
  float3 pmax = aabb.pmax;

  float3 l3 = pmax - pmin;

  int3 id[3];

  id[0].x = (int)((v[0].x - pmin.x) / l3.x * Dim.x);
  id[0].y = (int)((v[0].y - pmin.y) / l3.y * Dim.y);
  id[0].z = (int)((v[0].z - pmin.z) / l3.z * Dim.z);

  id[1].x = (int)((v[1].x - pmin.x) / l3.x * Dim.x);
  id[1].y = (int)((v[1].y - pmin.y) / l3.y * Dim.y);
  id[1].z = (int)((v[1].z - pmin.z) / l3.z * Dim.z);

  id[2].x = (int)((v[2].x - pmin.x) / l3.x * Dim.x);
  id[2].y = (int)((v[2].y - pmin.y) / l3.y * Dim.y);
  id[2].z = (int)((v[2].z - pmin.z) / l3.z * Dim.z);

  if (id[0].x == id[1].x && id[0].x == id[2].x &&
      id[0].y == id[1].y && id[0].y == id[2].y &&
      id[0].z == id[1].z && id[0].z == id[2].z)
      return id[0];
  else
      return i3(-1, -1, -1);
}

SDH_CALL 
float3 Diagonal(const S_Box & b)
{

	return b.pmax - b.pmin;
}

SDH_CALL
float Surface(const S_Box & b)
{
	float3 l = Diagonal(b);
	float  s = (l.x * l.y + l.y * l.z + l.z * l.x) * 2;

	return s;
}

SDH_CALL
float Volume(const S_Box & b)
{
	float3 l = Diagonal(b);
	float  v = l.x * l.y * l.z;

	return v;
}

#endif // _BOX_H_